/*
  TwoWire.h - TWI/I2C library for Arduino & Wiring
  Copyright (c) 2006 Nicholas Zambetti.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef Wire_h
#define Wire_h

#include <sys/types.h>

#include "Arduino.h"
#include "Stream.h"
#include "CircularBuffer.h"

typedef enum : uint8_t {
	I2C_EVENT_NONE    = 0x00,
	I2C_EVENT_IDLE    = 0x01,
	I2C_EVENT_TX_HALF = 0x02,
	I2C_EVENT_TX_TC   = 0x04,
	I2C_EVENT_RX_HALF = 0x08,
	I2C_EVENT_RX_TC   = 0x10,
} I2CEvent_t;

class TwoWire : Stream {
public:
	TwoWire(void *i2c, size_t bufferSize = I2C_BUFFER_SIZE);
	~TwoWire();

	bool begin(uint8_t devAddr, int sda = -1, int scl = -1);

	int readBit(uint16_t devAddr, uint16_t regAddr, uint8_t bitNb, uint8_t *data);
	int readBitW(uint16_t devAddr, uint16_t regAddr, uint8_t bitNb, uint8_t *data);
	int readBits(uint16_t devAddr, uint16_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data);
	int readByte(uint16_t devAddr, uint16_t regAddr, uint8_t *data);
	int readByteWord(uint16_t devAddr, uint16_t regAddr, uint8_t *data);
	int readBytes(uint16_t devAddr, uint16_t regAddr, uint8_t *data, uint8_t length);
	int readWords(uint16_t devAddr, uint16_t regAddr, uint16_t *data, uint8_t length);

	int writeBit(uint16_t devAddr, uint16_t regAddr, uint8_t bitNb, uint8_t data);
	int writeBits(uint16_t devAddr, uint16_t regAddr, uint8_t bitStart, uint8_t data);
	int writeByte(uint16_t devAddr, uint16_t regAddr, uint8_t data);
	int writeByteWord(uint16_t devAddr, uint16_t regAddr, uint16_t data);
	int writeBytes(uint16_t devAddr, uint16_t regAddr, uint8_t *data, uint8_t length);
	int writeWords(uint16_t devAddr, uint16_t regAddr, uint16_t *data, uint8_t length);

public:
	void beginTransmission(uint16_t devAddr, uint16_t regAddr, uint8_t addrSize = I2C_8BIT_ADDR);
	int endTransmission(bool sendStop = true);

	size_t requestFrom(uint16_t devAddr, size_t size, bool sendStop = true);
	size_t requestFromRegister(uint16_t devAddr, uint16_t regAddr, uint8_t addrSize, size_t length);

	virtual int available() { return (int) rxBuffer.available(); }

	virtual int peek() { return rxBuffer.peek(); }
	virtual int read();
	virtual size_t read(uint8_t *buffer, size_t length);
	virtual size_t write(uint8_t data);
	virtual size_t write(uint8_t *data, size_t size);

	void irqHandler(void *i2c, int event, uint32_t idleRxCount);

private:
	/**
	 * Startup DMA channel
	 * @param written_len Last time channel transferred data size
	 * @return If success startup dma return true, else return false
	 */
	bool writeToI2c(uint32_t written_len);

private:
	void *_i2c        = nullptr;
	uint16_t _devAddr = 0;
	uint16_t _regAddr;
	uint8_t _addrSize = I2C_8BIT_ADDR;
	bool nonStop;
	uint32_t timeOutMillis;

	CircularBuffer txBuffer;
	CircularBuffer rxBuffer;
};

#if I2C_1_ENABLE
extern TwoWire Wire;
#endif

#if I2C_2_ENABLE
extern TwoWire Wire2;
#endif

#endif //Wire_h
